﻿package microframe.abstract
{
	import com.greensock.TweenMax;
	import com.greensock.data.TweenMaxVars;
	import com.ux.base.BLoader;
	import com.ux.control.SoundPlayer;
	import com.ux.manager.StageManager;
	import com.ux.util.ParamUtil;
	import com.ux.util.StringUtils;
	
	import flash.display.DisplayObject;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.events.TimerEvent;
	import flash.system.Security;
	import flash.system.System;
	import flash.utils.Dictionary;
	import flash.utils.Timer;
	import flash.utils.setTimeout;
	
	import microframe.event.EventType;
	import microframe.event.MicroEvent;
	import microframe.event.MicroEventObserver;
	import microframe.interfaces.IContents;
	import microframe.manager.AutoRunManager;
	import microframe.manager.ContentsTypeManager;
	import microframe.manager.DATAManager;
	import microframe.manager.URLManager;
	import microframe.ui.ContentsContainer;
	import microframe.ui.Loading;
	
	
	/**
	 * @author winnie
	 */
	public class AbsApplication extends AbsContents
	{
		// fade in out effect
		private var _initTweenVars : TweenMaxVars;
		private var _fadeOutTweenVars : TweenMaxVars;
		private var _fadeInTweenVars : TweenMaxVars;
		private var _fadeOutDuration : Number;
		private var _fadeInDuration : Number;
		
		// contents
		/**
		 * 예약된 페이지 이름 : ContentsTypeManager 에 등록되어 있는 이름에 근거한다. 
		 */		
		protected var proxyPage : String;
		
		/**
		 * 현재 페이지 이름 : ContentsTypeManager 에 등록되어 있는 이름에 근거한다. 
		 */		
		protected var currentPage : String;
		
		/**
		 * current 바로 전 페이지 
		 */		
		protected var prevPage : String;
		
		/**
		 * 컨텐츠 로더 딕셔너리 객체 
		 */		
		protected var dicLoader : Dictionary = new Dictionary( true );
		
		/**
		 * isLogin => 로그인 체크 여부
		 * page => 변경될 페이지 ( 대소문자 구별 없음 , ContentsTypeManager 에 근거한 페이지 )
		 */		
		protected var param : Object;
		
		/**
		 * sound - 무비클립 스킨 등록 가능. ( off 시 1프레임으로 무브프레임, on 시 totlaframe 으로 무브프레임 )
		 */		
		protected var sound : SoundPlayer;

		/**
		 * 사용자에 의해 사운드가 켜졌는지 여부 
		 */		
		protected var isSoundUserON : Boolean = true;
		
		/**
		 * autoRun index
		 */		
		protected var autoRunIdx : int = 0;
		
		/**
		 * autoRun timer
		 */		
		protected var autoRunTimer : Timer;
		
		
		
		/**
		 * AbsApplication
		 */		
		public function AbsApplication()
		{
			super();
			
			// 메인이므로 최초 등록시킴.
			StageManager.stage = stage;
			
			// 도메인 셋팅 관련
			Security.allowDomain("*");
			
			// 외부 파라미터 셋팅
			parameterParsing();
			
			// 컨테이너에 기본 컨텐츠 셋팅.
			setContainerContents();
			
			
			if( URLManager.url( "sound" ) != "" )
			{
				sound = new SoundPlayer( URLManager.url( "sound" ) );
			}
		}
		
		
		private function setContainerContents():void
		{
			var len:int = this.numChildren;
			for (var i:int = 0; i < len; i++) 
			{
				ContentsContainer.container.addChild( this.getChildAt( 0 ) );
			}
			
			// 컨테이너 붙임.
			addChild( ContentsContainer.container );
		}
		
		
		/**
		 * isLogin => 로그인 체크 여부
		 * DATAManager 의 isLogin : Boolean, loginId : String 에 담겨진다. 
		 * 
		 * page => 변경될 페이지 ( 대소문자 구별 없음 , ContentsTypeManager 에 근거한 페이지 )
		 * page 는 proxyPage 변수에 담겨지며, 로딩이 완료되면 application 을 상속받은 자의  init 에서 실행 하면 된다.
		 */		
		protected function parameterParsing():void
		{
			param = StageManager.stage.loaderInfo.parameters;
			
			if( param.onLoginId != "" &&
				param.onLoginId != null &&
				param.onLoginId != "undefined" &&
				param.onLoginId != "null" &&
				param.onLoginId != undefined
			)
			{
				DATAManager.isLogin = true;
				DATAManager.loginId = String(param.onLoginId);
			}
			
			trace(" ==> AbsApplication parameterParsing : " + DATAManager.loginId );
			
			
			if( param.page != "" &&
				param.page != null &&
				param.page != "undefined" &&
				param.page != "null" &&
				param.page != undefined
			)
			{
				var len:int = ContentsTypeManager.pageList.length;
				for (var i:int = 0; i < len; i++) 
				{
					if( StringUtils.contains( ContentsTypeManager.pageList[i].menuName.toLowerCase(), String(param.page).toLowerCase() ) )
					{
						proxyPage = ContentsTypeManager.pageList[i].menuName;
						break;
					}
				}
			}
			
		}
		
		
		/**
		 * @param e 
		 */		
		override protected function onStage(e:Event):void
		{
			super.onStage( e );
			
			Security.allowDomain("*");
			StageManager.stage.align = StageAlign.TOP_LEFT;
			StageManager.stage.scaleMode = StageScaleMode.NO_SCALE;
		}
		
		
		
		/**
		 * <br> 페이지 변경 에 따른 이벤트 발생 </br>
		 * <br> MicroEventObserver 에 </br>
		 * <br>  EventType.ON_PAGE_CHANGE 로 등록된 Listener 자동 실행. </br>
		 * @param page : ContentsTypeManager에 등록된 페이지 명 전달.
		 */		
		public function contentsChange( page:String ):void
		{
			if( page == DATAManager.currentPage || page == null ) return;
			setCurrentPage( page );
		}
		
		
		/**
		 * <br> 2. 컨텐츠 체인지를 위한 두번째 단계 </br>
		 * <br> -> 한번 로드가 된 것인지, 최초인지 판별 후  contentsLoaded로 간다. </br>
		 */		
		private function loadAndCheckContents():void
		{
			if( prevPage ) removeExistContents( prevPage );
			setTimeout( nextLoad , fadeOutDuration*1000, DATAManager.currentPage );
		}
		
		private function nextLoad( recievePage : String ):void
		{
			if( recievePage != DATAManager.currentPage ) return;
			
			if( dicLoader[ DATAManager.currentPage ] && ( dicLoader[ DATAManager.currentPage ] as BLoader ).loadData != null )
			{
				contentsLoaded();
			}
			else
			{
				Loading.ON();
				dicLoader[ DATAManager.currentPage ] = new BLoader( URLManager.url( DATAManager.currentPage ), null, contentsLoaded, loadingFn );
			}
		}
		
		/**
		 * @param 최초의 delayTime 1000 / 1초
		 * @param idx 지정된 인덱스 부터 오토런 실행
		 */		
		protected function autoRunON( initDelayTime : Number = 2000 ):void
		{
			DATAManager.isAutoRun = true;
			MicroEventObserver.instance.addEventListener( EventType.ON_SUB_PAGE_CHANGE, onPageChangeForAutorun );
			
			// 현재 페이지부터 오토런 실행
			autoRunIdx = getAutorunIndex();
			setTimeout( onPageChangeForAutorun, initDelayTime );
			
			// 바로 다음페이지로 넘어감
//			onAutorunTimerComplete( null );
		}
		
		/**
		 * @param e : MicroEvent - 페이지 변경 시점부터 타이머 실행
		 */		
		protected function onPageChangeForAutorun( e:MicroEvent=null ):void
		{
//			trace( " ==> 오토런 인덱스 : " + autoRunIdx ,AutoRunManager.autuRunList[autoRunIdx].menuName );

			if( DATAManager.isAutoRun )
			{
				if( autoRunTimer == null )
				{
					autoRunTimer = new Timer( AutoRunManager.autuRunList[autoRunIdx].delayTime, 1 );
					autoRunTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onAutorunTimerComplete );
				}
				else
				{
					autoRunTimer.delay = AutoRunManager.autuRunList[autoRunIdx].delayTime;
				}
				
				if( AutoRunManager.autuRunList[autoRunIdx].fn != null ) AutoRunManager.autuRunList[autoRunIdx].fn(); 
				autoRunTimer.start();
			}
		}
		
		
		
		/**
		 * 실제 오토런 실행 하는 부분.
		 */		
		protected function playAutoRun():void
		{
			var preIdx :int = getPrevAutorunIndex();
			if( AutoRunManager.autuRunList[ preIdx ].menuName == AutoRunManager.autuRunList[ autoRunIdx ].menuName )
			{
				onPageChangeForAutorun( null );
			}
			else
			{
				this.contentsChange( AutoRunManager.autuRunList[autoRunIdx].menuName );
			}
		}
		
		/**
		 * @param e 오토런 다음 실행
		 */		
		protected function onAutorunTimerComplete( e:TimerEvent=null ):void
		{
			autoRunIdx++;
			
			if( autoRunIdx >= AutoRunManager.autuRunList.length ) autoRunIdx = 0;
			
			playAutoRun();
		}
		
		/**
		 * 오토런 끄기
		 */		
		protected function autoRunOFF():void
		{
			DATAManager.isAutoRun = false;
			if( autoRunTimer ) autoRunTimer.stop();
			MicroEventObserver.instance.removeEventListener( EventType.ON_SUB_PAGE_CHANGE, onPageChangeForAutorun );
		}
		
		
		private function getAutorunIndex():int
		{
			var idx : int = 0;
			var len:int = AutoRunManager.autuRunList.length;
			for (var i:int = 0; i < len; i++) 
			{
				if( 
					DATAManager.currentPage == AutoRunManager.autuRunList[i].menuName ||
					DATAManager.currentSubPage == AutoRunManager.autuRunList[i].menuName
				)
				{
					idx = i;
					return idx;
				}
			}
			
			return idx;
		}
		
		private function getPrevAutorunIndex():int
		{
			var prevIndex : int = autoRunIdx -1;
			if( prevIndex == -1 ) prevIndex = AutoRunManager.autuRunList.length-1;
			
			return prevIndex;
		}
		
		
		
		
		
		/**
		 * 로딩 상태 표시  
		 */		
		private function loadingFn():void
		{
			if( dicLoader[ DATAManager.currentPage ] != null ) Loading.LOADING( (dicLoader[ DATAManager.currentPage ] as BLoader).loadingPercentage );
		}
		
		
		/**
		 * <br> 1. 컨텐츠 체인지를 위한 첫번째 단계</br>
		 * <br>  -> loadAndCheckContents 로 간다.</br>
		 * 
		 * @param page 페이지 명 전달.
		 * @return 페이지 인덱스 전달.
		 */		
		private function setCurrentPage( page:String ):void
		{
			var len:int = ContentsTypeManager.pageList.length;
			for (var i:int = 0; i < len; i++) 
			{
				if( StringUtils.contains( ContentsTypeManager.pageList[i].menuName, page ) )
				{
					// 만약 바꾸려는 page 가 메인일 경우
					if( ContentsTypeManager.pageList[i].parentContentsData == null )
					{
						prevPage = DATAManager.currentPage;
						DATAManager.prevPage = prevPage;
						DATAManager.currentPage = page;
						DATAManager.currentSubPage = null;
						
						MicroEventObserver.executeListener( EventType.ON_PAGE_CHANGE );
						MicroEventObserver.instance.dispatchEvent(new MicroEvent( EventType.ON_PAGE_CHANGE, page ) );
						
						loadAndCheckContents();
					}
					// 바꾸려는 page가 서브일 경우
					else
					{
						// 넘어온 서브 페이지의 메인이 현재 메인 페이지와 같은 경우
						if( DATAManager.currentPage == ContentsTypeManager.pageList[i].parentContentsData.menuName )
						{
							DATAManager.currentSubPage = page;
							MicroEventObserver.executeListener( EventType.ON_SUB_PAGE_CHANGE );
							MicroEventObserver.instance.dispatchEvent(new MicroEvent( EventType.ON_SUB_PAGE_CHANGE, page ) );
						}
						// 메인도 완전 다른 서브 페이지가 넘어왔을 경우
						else
						{
							prevPage = DATAManager.currentPage;
							DATAManager.prevPage = prevPage;
							DATAManager.currentPage = ContentsTypeManager.pageList[i].parentContentsData.menuName;
							DATAManager.currentSubPage = page;
							
							MicroEventObserver.executeListener( EventType.ON_PAGE_CHANGE );
							MicroEventObserver.instance.dispatchEvent(new MicroEvent( EventType.ON_PAGE_CHANGE, page ) );
							
							loadAndCheckContents();
						}
						 
						
					}
					
					
//					trace(" ==> 페이지 변경 현재 메인 페이지는 : " + DATAManager.currentPage + "  , 현재 서브페이지는 : " + DATAManager.currentSubPage );

					return;
				}
			}
			
			
			throw new Error( " 'AbsApplication setCurrentPage' [ "+page+" ] 지정하신 페이지가 존재하지 않습니다. " );

		}
		
		
		
		
		/**
		 * 3. 컨텐츠 체인지 중 상속여부 체크 및 효과 적용을 위한 초기화, 리사이징 처리 등.
		 */		
		protected function contentsLoaded():void
		{
			Loading.OFF();
			if( dicLoader[ DATAManager.currentPage ] == null ) return;
			
			if( !( dicLoader[ DATAManager.currentPage ] as BLoader ).content as IContents )
			{
				throw new Error( "로드된 컨텐츠가 AbsContents 를 상속받지 않았습니다." );
				(dicLoader[ DATAManager.currentPage ] as BLoader ).allLoadStop();
				return;
			}
			
			MicroEventObserver.instance.dispatchEvent( new MicroEvent( EventType.ON_PAGE_CHANGE_LOAD_COMPLETE, DATAManager.currentPage ) );
			MicroEventObserver.executeListener( EventType.ON_PAGE_CHANGE_LOAD_COMPLETE );
			
			var contents : IContents = ( dicLoader[ DATAManager.currentPage ] as BLoader ).content as IContents;
			
			if( contents == null ) return;
			registerContent( contents as DisplayObject, contents.position );
			onResize();
			
			TweenMax.killTweensOf( contents );
			initTweenVars.onComplete( addContents, [(contents as IContents)] );
			TweenMax.to( (contents as DisplayObject), 0, initTweenVars );
			
		}
		
		
		
		
		/**
		 * 2. 로드된 컨텐츠 초기 effect 주기완료 및 컨텐츠 붙이기. 
		 * @param 컨텐츠 등장 후, 초기 실행.
		 */		
		private function addContents( contents:IContents):void
		{
			ContentsContainer.container.addChild( contents as DisplayObject );
			TweenMax.killTweensOf( contents );
			fadeInTweenVars.onComplete(onAddComplete, [(contents as IContents)] );
			TweenMax.to( contents, fadeInDuration, fadeInTweenVars );
			
			MicroEventObserver.instance.dispatchEvent( new MicroEvent( EventType.ON_PAGE_CHANGE_COMPLETE, DATAManager.currentPage ) );
			MicroEventObserver.executeListener( EventType.ON_PAGE_CHANGE_COMPLETE );
		}
		
		
		
		
		/**
		 * 3. 붙인 컨텐츠 실행.
		 * @param 컨텐츠 등장 후, 초기 실행.
		 */		
		private function onAddComplete(contents:IContents):void
		{
			if( ( contents as DisplayObject ).stage == null ) return;
			contents.init();
			MicroEventObserver.instance.dispatchEvent( new MicroEvent( EventType.ON_SUB_PAGE_CHANGE, DATAManager.currentSubPage ) );
			MicroEventObserver.executeListener( EventType.ON_SUB_PAGE_CHANGE );
		}
		
		
		
		
		
		/**
		 * 1. 컨텐츠 제거를 위한 메서드.
		 */		
		protected function removeExistContents( page:String ):void
		{
			if( dicLoader[ page ] == null || ( dicLoader[ page ] as BLoader ).content == null ) return;
			
			var contents :IContents = ( dicLoader[ page ] as BLoader ).content as IContents;

			TweenMax.killTweensOf( contents as DisplayObject );
			fadeOutTweenVars.onComplete(onRemoveComplete,[( contents as IContents )]);
			TweenMax.to( ( contents as DisplayObject ), fadeOutDuration, fadeOutTweenVars );
			
		}
		
		
		
		
		
		/**
		 * 2. 제거한 컨텐츠 메모리 제거.
		 * @param 실제로 contents 컨텐츠 메모리 제거 및 스테이지에서 제거하는 메서드
		 */		
		private function onRemoveComplete( contents:IContents ):void
		{
			removeContent( contents as DisplayObject );
			if( contents == null ) return;
			
			if( ( contents as DisplayObject ).stage != null )
			{
				ContentsContainer.container.removeChild( contents as DisplayObject );
			}

			contents.destory();
			
			MicroEventObserver.instance.dispatchEvent( new MicroEvent( EventType.ON_PAGE_REMOVE_COMPLETE, contents.contentsType ) );
			MicroEventObserver.executeListener( EventType.ON_PAGE_REMOVE_COMPLETE );
		}
		
		
		
		
		/**
		 * @param vars 컨텐츠 addChild 되기 전 최초 effect 등록
		 */		
		public function set initTweenVars( vars:TweenMaxVars ):void
		{
			_initTweenVars = vars;
		}
		
		/**
		 * @param vars 컨텐츠 removeChild 되기 전 effect 등록
		 */	
		public function set fadeOutTweenVars( vars:TweenMaxVars ):void
		{
			_fadeOutTweenVars = vars;
		}
		
		/**
		 * @param vars 컨텐츠 removeChild 되기 전 effects duration 등록
		 */	
		public function set fadeOutDuration( duration:Number ):void
		{
			_fadeOutDuration = duration;
		}
		
		/**
		 * @param vars 컨텐츠 addChild 후 effect 등록
		 */	
		public function set fadeInTweenVars( vars:TweenMaxVars ):void
		{
			_fadeInTweenVars = vars;
		}
		
		/**
		 * @param vars 컨텐츠 addChild 후 effect duration 등록
		 */
		public function set fadeInDuration( duration:Number ):void
		{
			_fadeInDuration = duration;
		}
		
		/**
		 * @param vars 컨텐츠 addChild 되기 전 최초 effect 반환
		 */	
		public function get initTweenVars():TweenMaxVars
		{
			if( !_initTweenVars )
			{
				_initTweenVars = new TweenMaxVars();
				_initTweenVars.autoAlpha( 0 );
			}
			return _initTweenVars;
		}
		
		/**
		 * @param vars 컨텐츠 removeChild 되기 전 effect 반환
		 */	
		public function get fadeOutTweenVars():TweenMaxVars
		{
			if( !_fadeOutTweenVars )
			{
				_fadeOutTweenVars = new TweenMaxVars();
				_fadeOutTweenVars.autoAlpha( 0 );
			}
			return _fadeOutTweenVars;
		}
		
		/**
		 * @param vars 컨텐츠 removeChild 되기 전 effects duration 반환
		 */	
		public function get fadeOutDuration():Number
		{
			if( !_fadeOutDuration )
			{
				_fadeOutDuration = 0.5;
			}
			return _fadeOutDuration;
		}
		
		/**
		 * @param vars 컨텐츠 addChild 후 effect 반환
		 */	
		public function get fadeInTweenVars():TweenMaxVars
		{
			if( !_fadeInTweenVars )
			{
				_fadeInTweenVars = new TweenMaxVars();
				_fadeInTweenVars.autoAlpha( 1 );
			}
			return _fadeInTweenVars;
		}
		
		/**
		 * @param vars 컨텐츠 addChild 후 effect duration 반환
		 */
		public function get fadeInDuration():Number
		{
			if( !_fadeInDuration )
			{
				_fadeInDuration = 0.8;
			}
			return _fadeInDuration;
		}
		
		
		
		
	}
}












